package jmetal.metaheuristics.nsgaII;

import java.util.Comparator;

import jmetal.core.Algorithm;
import jmetal.core.Operator;
import jmetal.core.Problem;
import jmetal.core.Solution;
import jmetal.core.SolutionSet;
import jmetal.util.JMException;
import jmetal.util.Ranking;
import jmetal.util.SingletonExtremePoint;
import jmetal.util.comparators.FitnessComparator;
import jmetal.util.dir.DirFitness;

/**
 * 在NSGAII的基础上将crowd distance改为占有最近参考线的角度和的大小做选择
 */

public class AngleDIR extends Algorithm {
	private static final long serialVersionUID = 7807605182441833698L;
	int populationSize;
	int maxEvaluations;
	int evaluations;
	
	Operator mutationOperator;
	Operator crossoverOperator;
	Operator selectionOperator;
	SolutionSet population;
	
	/**
	 * Constructor
	 * 
	 * @param problem
	 *            Problem to solve
	 */
	public AngleDIR(Problem problem) {
		super(problem);
	} 

	/**
	 * Runs the NSGA-II algorithm.
	 * 
	 * @return a <code>SolutionSet</code> that is a set of non dominated
	 *         solutions as a result of the algorithm execution
	 * @throws JMException
	 */
	public SolutionSet execute() throws JMException, ClassNotFoundException {
		initAlgorithm();
		
		initPopulation();		
		
		generations();
		
		SingletonExtremePoint.freePoint();
		
		// Return the first non-dominated front
		Ranking ranking = new Ranking(population);
		return ranking.getSubfront(0);
	} //   execute

	private void generations() throws JMException {
		// Generations
//		int generations = 0;
		while (evaluations < maxEvaluations) {
			SolutionSet offspringPopulation = generateOffspring();

			// Create the solutionSet union of solutionSet and offSpring
			SolutionSet union = ((SolutionSet) population).union(offspringPopulation);

			// Ranking the union
			Ranking ranking = new Ranking(union);

			int remain = populationSize;
			int index = 0;
			SolutionSet front = ranking.getSubfront(index);
			population.clear();

			while ((remain > 0) && (remain >= front.size())) {
				for (int k = 0; k < front.size(); k++) {
					population.add(front.get(k));
				} // for
				remain = remain - front.size();

				// Obtain the next front
				index++;
				if (remain > 0) {
					front = ranking.getSubfront(index);
				} // if
			} // while

			// Remain is less than front(index).size, insert only the best one
			if (remain > 0) { // front contains individuals to insert
				SolutionSet testSet = population.union(front);
				DirFitness fitness = new DirFitness(testSet, populationSize,problem_);
				fitness.assignAlgleFitness();
				
				@SuppressWarnings("rawtypes")
				Comparator comparator = new FitnessComparator();
				front.sort(comparator);
				
				for(int i = 0;i<remain;i++)
					population.add(front.get(i));
				
			} // if
//			System.out.println(generations++);
		} // while
		
	}

	private SolutionSet generateOffspring() throws JMException {
		DirFitness fitness = new DirFitness(population, populationSize,problem_);
		fitness.assignAlgleFitness();
		SolutionSet offspringPopulation = new SolutionSet(populationSize);
		Solution[] parents = new Solution[2];
		for (int i = 0; i < (populationSize / 2); i++) {
			if (evaluations < maxEvaluations) {
				// obtain parents
				parents[0] = (Solution) selectionOperator.execute(population);
				parents[1] = (Solution) selectionOperator.execute(population);
				Solution[] offSpring = (Solution[]) crossoverOperator.execute(parents);
				mutationOperator.execute(offSpring[0]);
				mutationOperator.execute(offSpring[1]);
				problem_.evaluate(offSpring[0]);
				problem_.evaluateConstraints(offSpring[0]);
				problem_.evaluate(offSpring[1]);
				problem_.evaluateConstraints(offSpring[1]);
				offspringPopulation.add(offSpring[0]);
				offspringPopulation.add(offSpring[1]);
				evaluations += 2;
			} // if
		} // for
		return offspringPopulation;
	}

	private void initPopulation() throws ClassNotFoundException, JMException {
		Solution newSolution;
		for (int i = 0; i < populationSize; i++) {
			newSolution = new Solution(problem_);
			problem_.evaluate(newSolution);
			problem_.evaluateConstraints(newSolution);
			evaluations++;
			population.add(newSolution);
		} 
	}

	
	/**
	 * 初始化参数
	 */
	private void initAlgorithm() {
		// Read the parameters
		populationSize = ((Integer) getInputParameter("populationSize")).intValue();
		maxEvaluations = ((Integer) getInputParameter("maxEvaluations")).intValue();

		// Initialize the variables
		population = new SolutionSet(populationSize);
		evaluations = 0;

		// Read the operators
		mutationOperator = operators_.get("mutation");
		crossoverOperator = operators_.get("crossover");
		selectionOperator = operators_.get("selection");
	}
}
